<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<meta http-equiv="X-UA-Compatible" content="ie=edge">
	<title>this</title>
</head>
<body>
<script type="text/javascript">
    //网址:http://www.jianshu.com/p/d647aa6d1ae6
    // 函数定义确定的事情:作用域的确定(永远不会改变)
    // 函数调用确定的事情:this指向,执行上下文的确定(随时可能改变,this关键字的值)
    //this是在函数调用时确定的,就是在执行上下文的创建阶段确定
    //函数在执行过程中,this一旦被确定就不可更改
    /*
var a = 10;
var obj = {
	a:20
}
function fn(){
	console.log(this);
	console.log(this.a);
}
fn();
fn.call(obj)

***********在一个函数上下文中,this由调动者提供,由调用函数的方式来决定.如果调用者函数的为一个对象所拥有,内部的this指向的是该对象,如果函数独立调用,该函数内部的this指向undefined,在非严格模式下,当this指向undefined时,它会自动指向全局对象*************
var a = 20;
function fn() {
	console.log(this)//window,严格模式下指向undefined
	function foo() {
		console.log(this)//window
		console.log(this.a);//严格模式这里报错
	}
	foo();
}
fn();
*******************************
var a = 20;
var obj = {
	a: 10,
	c: this.a + 20,//当obj在全局变量声明时,无论obj.c在什么地方调用,这里的this默认指向全局对象,当obj在函数环境中声明时,这里的this默认指向undefined,在非严格模式下指向window
	fn: function () {
		console.log(this);
		return this.a;
	}
}

console.log(obj.c);
console.log(window.obj.fn());
***********foo.getA()中，getA是调用者，他不是独立调用，被对象foo所拥有，因此它的this指向了foo。而test()作为调用者，尽管他与foo.getA的引用相同，但是它是独立调用的，因此this指向undefined，在非严格模式，自动转向全局window。*************
var a = 20;
var foo = {
	a: 10,
	getA: function () {
	    return this.a;
	}
}
console.log(foo.getA()); // 10

var test = foo.getA;
console.log(test());  // 20
****************************
function foo() {
	console.log(this.a)
}
function active(fn) {
	fn(); // 真实调用者，为独立调用
}
var a = 20;
var obj = {
	a: 10,
	getA: foo
}
active(obj.getA);

使用call和apply指定this
	*将类数组转换为数组
	function exam(a, b, c, d, e) {
	    console.log(arguments)
	    var arg = [].slice.call(arguments);// 使用call/apply将arguments转换为数组, 返回结果为数组，arguments自身不会改变
	    console.log(arg);
	}
	exam(2, 8, 9, 10, 3);
	result:
	{ '0': 2, '1': 8, '2': 9, '3': 10, '4': 3 }
	[ 2, 8, 9, 10, 3 ]
	也常常使用该方法将DOM中的nodelist转换为数组
	[].slice.call( document.getElementsByTagName('li') )
	*根据自己的需要灵活修改this指向
	var foo = {
		name:"xiaoxi",
		showName:function(){
			console.log(this.name);
		}
	}

	var bar = {
		name:"aa"
	}
	foo.showName.call(bar)
	*实现继承
	***在Student的构造函数中，借助call方法，将父级的构造函数执行了一次，相当于将Person中的代码，在Sudent中复制了一份，其中的this指向为从Student中new出来的实例对象。call方法保证了this的指向正确，因此就相当于实现了基层。Student的构造函数等同于下。*
	// 定义父级的构造函数
	var Person = function(name, age) {
	    this.name = name;
	    this.age  = age;
	    this.gender = ['man', 'woman'];
	}
	// 定义子类的构造函数
	var Student = function(name, age, high) {
	    // use call
	    Person.call(this, name, age);
	    this.high = high;
	}
	Student.prototype.message = function() {
	    console.log('name:'+this.name+', age:'+this.age+', high:'+this.high+', gender:'+this.gender[0]+';');
	}
	new Student('xiaom', 12, '150cm').message();
	// result
	// name:xiaom, age:12, high:150cm, gender:man;

	*在向其他执行上下文的传递中,确保this的指向保持不变
		借助闭包和apply方法,封装一个bind方法,改变this的指向
		function bind(fn,obj){
			return function(){
				return fn.apply(obj,arguments)
			}
		}
		var obj = {
		    a: 60,
		    getA: function() {
		        setTimeout(bind(function() {
		            console.log(this.a)
		        }, this), 1000)
		    }
		}
		obj.getA();
		当然，也可以使用ES5中已经自带的bind方法。它与我上面封装的bind方法是一样的效果。
		var obj = {
		    a: 20,
		    getA: function() {
		        setTimeout(function() {
		            console.log(this.a)
		        }.bind(this), 1000)
		    }
		}

		构造函数和原型方法上的this
		function Person(name, age) {
			console.log(this);
		    // 这里的this指向了谁?
		    this.name = name;
		    this.age = age;
		}

		Person.prototype.getName = function() {
			console.log(this);
		    // 这里的this又指向了谁？
		    return this.name;
		}

		// 上面的2个this，是同一个吗，他们是否指向了原型对象？
		var p1 = new Person('Nick', 20);
		p1.getName();

		* 通过new操作符调用构造函数，会经历以下4个阶段。

		创建一个新的对象；
		将构造函数的this指向这个新对象；
		指向构造函数的代码，为这个对象添加属性，方法等；
		返回新对象。
*/
// http://www.cnblogs.com/wangfupeng1988/p/3988422.html
//this的取值,是在函数被调用时才能被确定,函数定义时确定不了,this是执行上下文环境的一部分,每次调用函数,就会产生一个新的执行上下文环境
// 函数定义:确定函数体内部自由变量(作用域在程序运行前就已经确定)的作用域
// 自由变量:跨作用域的值都可以当做自由变量:在当前作用域中使用某个(x)变量,却没有在当前作用域下声明x变量(在其他作用域中声明),这样的变量称为自由变量
    // 自由变量的取值,应该去创建这个函数的作用域中去取值,创建而非调用 ==> 静态作用域
// 函数调用:确定执行上下文环境,普通函数(没有通过new)中的this指向window
// this取值的四种情况
// 1:构造函数(new):this指向将他new出来的对象
// 2:函数作为对象的一个属性:函数作为对象中的一个属性被调用时,函数中的this指向该对象
/*    var obj = {
        x:10,
        fn:function () {
            console.log(this.x)
        }
    }
    obj.fn()
    var fn2 = obj.fn
    fn2()//注意这里的this指向的是window fn函数被赋值到另一个变量中,并没有作为obj的一个属性被调用,他的this指向的是window*/
//3:函数通过call或者apply的调用:当一个函数被call和apply调用时,this指向传入对象的值
//4:全局或者调用普通函数:全局或者函数在调用是this指向都是window
    // 原型链中的this:指向当前对象的值

// 执行上下文栈
//执行全局代码时,会产生一个执行上下文环境,每次调用函数时又会产生一个执行上下文环境.当函数调用完成后,上下文环境以及其中的数据都会被清除,在重新返回全局上下文环境,处于活动状态的执行上下文环境只有一个

// 作用域在函数定义是创建,而不是在函数调用时创建
//作用域:js没有块级块级作用域,只有全局作用域和函数作用域
//作用域的最大用处是隔离变量,不同作用域下的同名变量不会有冲突
// 作用域只是一个'地盘',一个抽象的概念,其中并没有变量.需要通过作用域所对应的执行上下文环境来获取变量的值.同一个作用域下,不同 的调用会产生不同的执行上下文环境,继而产生不同变量的值.所以作用域变量的值在执行过程中产生确定的,而作用域是在函数创建时确立的   //如果要找到某一个变量对应的值,先确定这个作用域的执行上下文环境,再在其中寻找变量的值
    // 上下文环境:同一个函数作用域只有一个,上下文环境可能会有多个,可能一个也没有
</script>
</body>
</html>
